#include <windows.h>
#include <tchar.h>
#include "sysSettings.h"
#include "krnlTypes.h"

///////////////////////////////////////////////////////////////////////////////
// Const
#define guiSCREEN_WIDTH (uiSS_DIGIT_COUNT * (guiemuSS_DIGIT_WIDTH+guiemuSS_DIGIT_GAP))
#define guiSCREEN_HEIGHT guiemuSS_DIGIT_HEIGHT

///////////////////////////////////////////////////////////////////////////////
// Display data buffer
static BYTE l_data_buffer[uiSS_DIGIT_COUNT];

///////////////////////////////////////////////////////////////////////////////
// Module global variables
static DWORD	l_thread_id			= 0;
static HANDLE	l_thread_handle = NULL;
static HWND		l_hwnd					= 0;
static HBRUSH	l_back_brush		= NULL;

///////////////////////////////////////////////////////////////////////////////
// Function prototypes
static void ThreadRoutine( void* in_param );
static HWND CreateSevenSegmentEmuWindow(void);
static void UpdateEmulationWindow(HDC in_hdc);
extern void EmulatorProcessCharacter(dosBool in_extended, dosChar in_char);

///////////////////////////////////////////////////////////////////////////////
// Functions
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Init emulator window
void uiInitSevenSegmentDriver(void)
{
	l_thread_handle = CreateThread( NULL,
																	0,
																	(LPTHREAD_START_ROUTINE)ThreadRoutine,
																	NULL,
																	0,
																	&l_thread_id );

	l_data_buffer[0] = 0x37;
	l_data_buffer[1] = 0x60 | 0x80;
	//l_data_buffer[2] = 0x37;
	l_data_buffer[2] = 0x7b;
}

///////////////////////////////////////////////////////////////////////////////
// Postpone emulator thread
void guiDisableEmulatorThread(void)
{
	if( l_thread_handle != NULL )
		SuspendThread( l_thread_handle );
}

///////////////////////////////////////////////////////////////////////////////
// Resume thread
void guiEnableEmulatorThread(void)
{
	if( l_thread_handle != NULL )
		ResumeThread( l_thread_handle );
}

///////////////////////////////////////////////////////////////////////////////
// Update canvas
void guiUpdateCanvas( dosInt16 in_left, dosInt16 in_top, dosInt16 in_right, dosInt16 in_bottom )
{
	if( l_hwnd != NULL )
	{
		HDC hdc = GetDC( l_hwnd );

		if( hdc != NULL )
			UpdateEmulationWindow( hdc );

		ReleaseDC( l_hwnd, hdc );
	}
}

///////////////////////////////////////////////////////////////////////////////
// Is emulator running
dosBool IsEmulatorRunning(void)
{
	Sleep(0);

	return l_thread_handle != NULL;
}

///////////////////////////////////////////////////////////////////////////////
// Internal functions
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Window message handler
static LRESULT CALLBACK WindowMessageHandler(	HWND		in_hwnd,
																							UINT		in_message,
																							WPARAM	in_wparam,
																							LPARAM	in_lparam )
{
  switch (in_message)
  {
		case WM_CREATE:
		{
			// set size
			RECT work_area;
			RECT owner_rect;
			HWND owner_hwnd;
			RECT client_rect;
			RECT window_rect;
			int width, height;
			int left, top;

			// set window size
			GetClientRect(in_hwnd, &client_rect);
			GetWindowRect(in_hwnd, &window_rect );

			width = window_rect.right - window_rect.left;
			height = window_rect.bottom - window_rect.top;

			width		= guiSCREEN_WIDTH + 2 * guiemuSS_DISPLAY_BORDER + width - client_rect.right;
			height	= guiSCREEN_HEIGHT + 2 * guiemuSS_DISPLAY_BORDER + height - client_rect.bottom;

			owner_hwnd = GetDesktopWindow();

			GetWindowRect( owner_hwnd, &owner_rect );

			left = ( owner_rect.left + owner_rect.right	- width		) / 2;
			top = ( owner_rect.top  + owner_rect.bottom	- height	) / 2;

			// center within screen coordinates
			SystemParametersInfo(SPI_GETWORKAREA, 0, &work_area, 0);

			if( left < work_area.left )
				left = work_area.left;

			if( top < work_area.top )
				top = work_area.top;

			if( left + width > work_area.right )
				left = work_area.right - width;

			if( top + height> work_area.bottom )
				top = work_area.bottom - height;

			SetWindowPos( in_hwnd,
										NULL,
										left,
										top,
										width, height,
										SWP_NOZORDER | SWP_NOACTIVATE);
		}
		break;

    case WM_PAINT:
			{
				PAINTSTRUCT ps;
				HDC hdc;

				hdc = BeginPaint(in_hwnd, &ps);

				UpdateEmulationWindow( hdc );

				EndPaint(in_hwnd, &ps);
			}
			break;

		case WM_DESTROY:
			PostQuitMessage(0);
      break;

		case WM_KEYDOWN:
			//EmulatorProcessCharacter( (in_lparam & (1<<24)) != 0, (dosChar)in_wparam );
			break;

		default:
      return DefWindowProc(in_hwnd, in_message, in_wparam, in_lparam);
      break;
    }

    return 0;
}

///////////////////////////////////////////////////////////////////////////////
// Update emulation window
static void UpdateEmulationWindow(HDC in_hdc )
{
	RECT rc;
	HBRUSH lit_brush;
	HBRUSH dark_brush;
	POINT poly[6];
	int middle;
	BYTE segment;
	HBRUSH old_brush;
	HPEN new_pen;
	HPEN old_pen;
	int digit;

	new_pen = CreatePen( PS_NULL, 0, RGB(0,0,0) );
	lit_brush = CreateSolidBrush(guiemuSS_SEGMENT_LIT_COLOR);
	dark_brush = CreateSolidBrush(guiemuSS_SEGMENT_DARK_COLOR);
	
	old_brush = (HBRUSH)SelectObject(in_hdc, lit_brush);
	old_pen = (HPEN)SelectObject( in_hdc, new_pen );
	
	for( digit = 0; digit < uiSS_DIGIT_COUNT; digit++ )
	{
		rc.left = guiemuSS_DISPLAY_BORDER + digit * (guiemuSS_DIGIT_WIDTH + guiemuSS_DIGIT_GAP);
		rc.top = guiemuSS_DISPLAY_BORDER;
		rc.right = rc.left + guiemuSS_DIGIT_WIDTH;
		rc.bottom = rc.top + guiemuSS_DIGIT_HEIGHT;
		
		middle = (rc.top + rc.bottom) / 2;

		segment = l_data_buffer[digit];

#ifdef guiemuSS_DP_BEFORE
		if( (segment & 0x80) != 0 )
			SelectObject( in_hdc, lit_brush );
		else
			SelectObject( in_hdc, dark_brush );

		Ellipse( in_hdc, rc.left + (guiemuSS_DIGIT_GAP - guiemuSS_SEGMENT_WIDTH)/2 - 1,
										 rc.bottom - guiemuSS_SEGMENT_WIDTH - 1,
										 rc.left + (guiemuSS_DIGIT_GAP + guiemuSS_SEGMENT_WIDTH)/2 + 1,
										 rc.bottom + 1 );

		rc.left += guiemuSS_DIGIT_GAP;
		rc.right += guiemuSS_DIGIT_GAP;
#endif

		// draw segments

		///////////////////> A <///////////////////////////
		///    0---------------------------1
		//        3--------------------2
		//
		poly[0].x = rc.left + guiemuSS_SEGMENT_GAP;														poly[0].y = rc.top;
		poly[1].x = rc.right - guiemuSS_SEGMENT_GAP;													poly[1].y = rc.top;
		poly[2].x = rc.right - guiemuSS_SEGMENT_WIDTH - guiemuSS_SEGMENT_GAP;	poly[2].y = rc.top + guiemuSS_SEGMENT_WIDTH;
		poly[3].x = rc.left + guiemuSS_SEGMENT_WIDTH + guiemuSS_SEGMENT_GAP;	poly[3].y = rc.top + guiemuSS_SEGMENT_WIDTH;

		if((segment & 0x01) != 0)
			SelectObject( in_hdc, lit_brush );
		else
			SelectObject( in_hdc, dark_brush);

		Polygon(in_hdc, poly, 4);
	 
		///////////////////> D <///////////////////////////
		//        0--------------------1
		///    3---------------------------2
		poly[0].x = rc.left + guiemuSS_SEGMENT_WIDTH + guiemuSS_SEGMENT_GAP;	poly[0].y = rc.bottom - (guiemuSS_SEGMENT_WIDTH);
		poly[1].x = rc.right - guiemuSS_SEGMENT_WIDTH - guiemuSS_SEGMENT_GAP;	poly[1].y = rc.bottom - (guiemuSS_SEGMENT_WIDTH);
		poly[2].x = rc.right - guiemuSS_SEGMENT_GAP;													poly[2].y = rc.bottom;
		poly[3].x = rc.left + guiemuSS_SEGMENT_GAP;														poly[3].y = rc.bottom;

		if( (segment & 0x02) != 0 )
			SelectObject( in_hdc, lit_brush );
		else
			SelectObject( in_hdc, dark_brush );

		Polygon( in_hdc, poly, 4 );

		///////////////////> G <///////////////////////////
		//        0--------------------1
		//     5---------------------------2
		//        4--------------------3
		// 
		poly[0].x = rc.left + guiemuSS_SEGMENT_WIDTH + guiemuSS_SEGMENT_GAP;	poly[0].y = middle - guiemuSS_SEGMENT_WIDTH/2;
		poly[1].x = rc.right - guiemuSS_SEGMENT_WIDTH - guiemuSS_SEGMENT_GAP;	poly[1].y = middle - guiemuSS_SEGMENT_WIDTH/2;
		poly[2].x = rc.right - guiemuSS_SEGMENT_GAP;													poly[2].y = middle;
		poly[3].x = rc.right - guiemuSS_SEGMENT_WIDTH - guiemuSS_SEGMENT_GAP;	poly[3].y = middle + guiemuSS_SEGMENT_WIDTH/2;
		poly[4].x = rc.left + guiemuSS_SEGMENT_WIDTH + guiemuSS_SEGMENT_GAP;	poly[4].y = middle + guiemuSS_SEGMENT_WIDTH/2;
		poly[5].x = rc.left + guiemuSS_SEGMENT_GAP;														poly[5].y = middle;

		if( (segment & 0x04) != 0 )
			SelectObject( in_hdc, lit_brush );
		else
			SelectObject( in_hdc, dark_brush );

		Polygon( in_hdc, poly, 6 );

		///////////////////> F <///////////////////////////
		//  0
		//  | 3
		//  | |
		//  | 2
		//  1
		poly[0].x = rc.left;													poly[0].y = rc.top + guiemuSS_SEGMENT_GAP;
		poly[1].x = rc.left;													poly[1].y = middle - guiemuSS_SEGMENT_GAP;
		poly[2].x = rc.left + guiemuSS_SEGMENT_WIDTH; poly[2].y = middle - guiemuSS_SEGMENT_WIDTH/2 - guiemuSS_SEGMENT_GAP;
		poly[3].x = rc.left + guiemuSS_SEGMENT_WIDTH; poly[3].y = rc.top + guiemuSS_SEGMENT_WIDTH + guiemuSS_SEGMENT_GAP;

		if( (segment & 0x08) != 0 )
			SelectObject( in_hdc, lit_brush );
		else
			SelectObject( in_hdc, dark_brush );

		Polygon( in_hdc, poly, 4 );

		///////////////////> E <///////////////////////////
		//  0   
		//  | 3 
		//  | |
		//  | 2  
		//  1       
		poly[0].y += middle - guiemuSS_DISPLAY_BORDER;
		poly[1].y += middle - guiemuSS_DISPLAY_BORDER;
		poly[2].y += middle - guiemuSS_SEGMENT_WIDTH/2 - guiemuSS_DISPLAY_BORDER;
		poly[3].y += middle - guiemuSS_SEGMENT_WIDTH/2 - guiemuSS_DISPLAY_BORDER;

		if( (segment & 0x10) != 0 )
			SelectObject( in_hdc, lit_brush );
		else
			SelectObject( in_hdc, dark_brush );

		Polygon( in_hdc, poly, 4 );

		///////////////////> B <///////////////////////////
		//    0
		//  3 |
		//  | |
		//  2 |
		//    1
		poly[0].x = rc.right - (guiemuSS_SEGMENT_WIDTH);	poly[0].y = rc.top + guiemuSS_SEGMENT_WIDTH + guiemuSS_SEGMENT_GAP;
		poly[1].x = rc.right - (guiemuSS_SEGMENT_WIDTH);	poly[1].y = middle - guiemuSS_SEGMENT_WIDTH/2 - guiemuSS_SEGMENT_GAP;
		poly[2].x = rc.right;															poly[2].y = middle - guiemuSS_SEGMENT_GAP;
		poly[3].x = rc.right;															poly[3].y = rc.top + guiemuSS_SEGMENT_GAP;

		if( (segment & 0x20) != 0 )
			SelectObject( in_hdc, lit_brush );
		else
			SelectObject( in_hdc, dark_brush );

		Polygon( in_hdc, poly, 4 );

		///////////////////> C <///////////////////////////
		//    3
		//  0 |
		//  | |
		//  1 | 
		//    2
		poly[0].y += middle - guiemuSS_SEGMENT_WIDTH/2 - guiemuSS_DISPLAY_BORDER;
		poly[1].y += middle - guiemuSS_SEGMENT_WIDTH/2 - guiemuSS_DISPLAY_BORDER;
		poly[2].y += middle - guiemuSS_DISPLAY_BORDER; 
		poly[3].y += middle - guiemuSS_DISPLAY_BORDER;

		if( (segment & 0x40) != 0 )
			SelectObject( in_hdc, lit_brush );
		else
			SelectObject( in_hdc, dark_brush );

		Polygon( in_hdc, poly, 4 );

		// Draw Decimal point
#ifdef guiemuSS_DP_AFTER
		if( (segment & 0x80) != 0 )
			SelectObject( in_hdc, lit_brush );
		else
			SelectObject( in_hdc, dark_brush );

		Ellipse( in_hdc, rc.right + (guiemuSS_DIGIT_GAP - guiemuSS_SEGMENT_WIDTH)/2 - 1,
										 rc.bottom - guiemuSS_SEGMENT_WIDTH - 1,
										 rc.right + (guiemuSS_DIGIT_GAP + guiemuSS_SEGMENT_WIDTH)/2 + 1,
										 rc.bottom + 1 );
#endif

	}

	// delete objects
	SelectObject( in_hdc, old_brush );
	SelectObject( in_hdc, old_pen );
	
	DeleteObject(dark_brush);
	DeleteObject(lit_brush);
}

///////////////////////////////////////////////////////////////////////////////
// Create window
static HWND CreateSevenSegmentEmuWindow(void)
{
	HWND hwnd;

	// register class
	WNDCLASSEX wcex;

	l_back_brush = CreateSolidBrush(guiemuSS_BACK_COLOR);

	wcex.cbSize					= sizeof(WNDCLASSEX);
	wcex.style          = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc    = WindowMessageHandler;
	wcex.cbClsExtra     = 0;
	wcex.cbWndExtra     = 0;
	wcex.hInstance      = 0;
	wcex.hIcon          = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
	wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground  = l_back_brush;
	wcex.lpszMenuName   = NULL;
	wcex.lpszClassName  = _T("dosSevenSegmentDisplayEmulationWindow");
	wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));

  if(!RegisterClassEx(&wcex))
  {
		MessageBox(NULL, _T("Call to RegisterClassEx failed!"), _T("dosEmuWin"), 0);

    return 0;
  }

	// create window
	hwnd= CreateWindow(	_T("dosSevenSegmentDisplayEmulationWindow"),
											_T("Domino Operation System Emulation"),
											WS_OVERLAPPEDWINDOW | WS_DLGFRAME | WS_SYSMENU | WS_MINIMIZEBOX,
											CW_USEDEFAULT, CW_USEDEFAULT,
											0, 0,
											NULL,
											NULL,
											0,
											NULL );
	if( !hwnd )
	{
		MessageBox(NULL, _T("Call to CreateWindow failed!"), _T("DominoOS Emu"), 0);

    return 0;
	}

	if( hwnd != 0 )
	{
		ShowWindow(hwnd, SW_SHOWNORMAL);
		UpdateWindow(hwnd);
	}

	return hwnd;
}

///////////////////////////////////////////////////////////////////////////////
// Thread functions
static void ThreadRoutine( void* in_param )
{
	MSG msg;
	
	l_hwnd = CreateSevenSegmentEmuWindow();

	if( l_hwnd != 0 )
	{
		// Main message loop:
		while (GetMessage(&msg, NULL, 0, 0)) 
		{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
				Sleep(0);
		}
	}

	if( l_hwnd != 0 )
		DestroyWindow( l_hwnd );
		
	if( l_back_brush != NULL )
		DeleteObject(l_back_brush);

	l_thread_id			= 0;
	l_thread_handle = NULL;

	ExitThread( 0 );
}